home *** CD-ROM | disk | FTP | other *** search
Text File | 1998-05-09 | 14.7 KB | 411 lines | [TEXT/ALFA] |
- ##############################################(nowrap)#############
- Alpha AppleEvent Packages
-
- created: 4/9/98 {10:32:09 PM}
- last update: 5/9/98 {11:56:57 AM}
- version: 1.1b3
- Author: Jonathan Guyer
- E-mail: <jguyer@his.com>
- www: <http://www.his.com/~jguyer/>
-
- Copyright (c) 1998 Jonathan Guyer
-
- ###################################################################
-
- This package is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License.
-
- ###################################################################
-
- Alpha's AEBuild command makes it (comparatively) easy to
- interface with other applications, through the traumatic world of
- AppleEvents (and Capture AE makes writing AE descriptors far easier
- than it used to be, particulary for some inarticulate applications
- (I won't mention any names, but a major offender rhymes with “Blinder”)).
- Using AEGizmos, one can build complicated event structures for
- interaction with other programs.
-
- Alpha automatically processes AppleEvent replies from those
- programs with AEPrint, which renders them reasonably
- human-readable, but quite unwieldy to deal with,
- programmatically. Programmers wishing to work with AE from
- within Alpha have been forced to write clumsy regexpen to attempt
- to extract the elements of interest from event replies, but
- because of the potential complexity of AE reply records, these
- regexpen almost invariably fail to account for all contingencies.
-
- To make it easier to work with AppleEvent replies (and, by
- extension, to process events for which Alpha has been declared a
- handler), I have written a suite of packages: aeparse.tcl,
- aecoerce.tcl, and aebuild.tcl. The first two packages work in concert
- to implement a reasonable facsimile of Jens Alfke's
- “formidable-looking BNF grammar specification”, parsing AEPrint
- strings into Tcl-readable lists of lists. The third package
- essentially replaces the routines in appleEvents.tcl with a more
- consistently named set of routines. Stub routines remain in
- appleEvents.tcl to map to the new format.
-
- Given an “aevt\ansr{...}”, a “GURL\GURL{...}”, or some subset of an
- AEGizmo string, you can parse to a list of lists; keyword searches
- are then easy.
-
- If you call:
-
- aeparse::event [AEBuild bladdity blah]
-
- you get back a typed and tagged list of lists. If the reply
- from [AEBuild bladdity blah] is:
-
- aevt\ansr{BLAH:5, blah:“Blah?”}
-
- then aeparse::event will return
-
- reco {{BLAH {long 5}} {blah {TEXT Blah?}}}
-
- All the extra tags may seem cumbersome, but the default behavior of
- the routines is to strip them out. For instance:
-
- aeparse::keywordValue blah [aeparse::event [AEBuild bladdity blah]]
-
- will just return:
-
- Blah?
-
- If you don't know in advance that the 'blah' keyword holds a string,
- then aeparse::keywordValue takes an optional typed flag, so you'll get back
-
- TEXT Blah?
-
- Since, for many event replies, the direct object is what's wanted,
- there is a short-hand to replace
-
- aeparse::keywordValue ---- [aeparse::event [AEBuild bladdity blah]]
-
- with
-
- aebuild::result bladdity blah
-
-
- In the event of AppleEvent errors, aeparse::event automatically checks
- for 'errn' and 'errs' tags and throws an appropriate error. The
- array 'aeparse::errors' holds error messages to be used when no
- 'errs' tag accompanies 'errn' (the Finder does this); see the
- initialization code for aeparse for examples of the format.
-
- In addition to parsing AE descriptor strings, calls to
- aeparse::event automatically perform any type coercions that have
- been declared with aecoerce::register. For example:
-
- aeparse::event {aevt\ansr{----:bool(«01»)}}
-
- will not return
-
- reco {{---- {bool {hexd 01}}}}
-
- but rather
-
- reco {{---- {bool 1}}}
-
- so that
-
- aeparse::keywordValue "----" \
- [aeparse::event {aevt\ansr{----:bool(«01»)}}]
-
- just returns “1”; no further processing is necessary.
-
- A more powerful example is seen in a rewrite of
- mailboxPathIndex in eudoraMenu.tcl:
-
- proc mailboxPathIndex {ind} {
- set res [objectProperty 'CSOm' euFS [mailboxByIndex $ind]]
- return [extractPath $res]
- }
-
- 'res' will hold something like
-
- aevt\ansr{'----':fss («FFFD000001CC02496E00002F52FE002F52827FFF7FFF7FFF7F0001C70FC00000F90001D3F9547FFF7FFF01C95F5001D33C5E01D33BFC004120B401D33C4401D3CCCCCCCCCCCC»)}
-
- extractPath looks for anything between « and » and attempts to
- coerce that from an 'fss ' to a path, returning
-
- documents:Eudora:HIS:In
-
- This example happens to work, but some gross assumptions have
- been made about the format of the returned AE descriptor string
- and explicit coercions are needed after every AEBuild call. Using
- the routines of this package, we can instead write:
-
- proc mailboxPathIndex {ind} {
- set res [aeparse::event [objectProperty 'CSOm' euFS [mailboxByIndex $ind]]]
- return [aeparse::keywordValue "----" $res]
- }
-
- In this case, 'res' holds
-
- reco {{---- {{fss } documents:Eudora:HIS:In}}}
-
- Although the code is not any shorter, the coercion of 'fss ' has
- been performed automatically (and not arbitrarily). Although
- this list of lists looks like a mess, it is easily processed by
- aeparse::keywordValue, so you need not worry about its
- internals. We can simplify 'mailboxPathIndex' even further by
- writing:
-
- proc mailboxPathIndex {ind} {
- return [aebuild::result 'CSOm' core getd ---- \
- [propertyObject euFS [mailboxByIndex $ind]]]
- }
-
- or, better yet,
-
- proc mailboxPathIndex {ind} {
- return [aebuild::objectProperty 'CSOm' euFS [mailboxByIndex $ind]]
- }
-
- CAVEAT: Because the AE descriptor string is being completely
- parsed, aeparse::event can be rather slower than blind regexping.
- The trade-off is that the results you obtain are much less
- likely to be erroneous, especially for error conditions. I will be
- looking for ways to speed things up, though.
- _________________________________________________________________
-
- Cheater's Guide to AppleEvent Descriptors
- _________________________________________________________________
-
- Until I found out about Capture AE (thanks Tom!), there was no simple
- way to figure out how to write an AEDesc for some applications (I've
- never learned anything useful from AETracker, but YMMV). Ironically,
- the Finder is one of the hardest, at least in part because its
- scripting model was developed concurrently with, and in some cases
- before, the OSA architecture as a whole was developed. Determining
- something as trivial as a list of the available disks which, in
- AppleScript, is
-
- tell application "Finder" to get disks
-
- can be an unholy nightmare to translate into AEGizmos. Fortunately,
- if you activate Capture AE, execution of this script dumps
-
- Process("Finder").SendAE "core,getd,'----':obj {form:indx, want:type(cdis), seld:abso(«616C6C20»), from:'null'()}"
-
- The '&inte' and '&timo' keys are not relevant to us, so this is
- easily enough rendered in Tcl as
-
- AEBuild -r 'MACS' core getd ---- {obj {form:indx, want:type(cdis), seld:abso(«616C6C20»), from:'null'()}}
-
- Although it works, "abso(«616C6C20»)" isn't very nice to look at. A
- quick pass with aecoerce::hexd:TEXT (or perusal of an ASCII chart)
- reveals this is, reasonably enough, the identifier 'all ', so we
- substitute that and make use of this suite of routines, yielding
-
- aebuild::result 'MACS' core getd ---- {obj {form:indx, want:type(cdis), seld:abso(all), from:'null'()}}
-
- which... unfortunately... returns
-
- {reco {{want {type prop}} {from {null {}}} {form {type prop}} {seld {type sdsk}}}} {reco {{want {type cdis}} {from {null {}}} {form {type name}} {seld {TEXT catbert}}}} {reco {{want {type cdis}} {from {null {}}} {form {type name}} {seld {TEXT ratbert}}}}}
-
- which is a parsing nightmare (note that, on top of everything else, the
- Startup Disk is treated differently from everything else). Fortunately,
- we can make the Finder do our work for us by using the 'rtyp' tag
-
- aebuild::result 'MACS' core getd rtyp TEXT ---- {obj {form:indx, want:type(cdis), seld:abso('all '), from:'null'()}}
-
- which yields a manageable
-
- catbert: dogbert: ratbert:
-
- on my computer.
-
-
- Even with Capture AE, it's handy to be able to read 'aete' resources
- (if for no other reason, to decipher what you just blindly copied
- into your AEBuild statements). For this, Yuji Hachiya's 'aete' editor
- for ResEdit is indispensible.
-
-
- If, for some reason, Capture AE is unavailable to you (redistribution
- is prohibited, so WestCodeSoft could conceivably withdraw it), there
- is, fortunately, a trick you can try:
-
- 1) Enter the above AppleScript in the Script Editor and save it as
- a compiled script.
- 2) Open the script in a resource editor, open the 'scpt' resources,
- and open what will likely be the only 'scpt' resource there
- (if you have a 'scpt' resource editor installed, you'll want to
- be sure to open the resource as hexadecimal).
- 3) Look for the first occurence of the string 'MACS' («4D414353»)
- and change this to 'ALFA' («414C4641»). Ignore any explicit
- references to "Finder"; they don't matter. You've just changed the
- script to ask Alpha for a list of all disks. Alpha obviously doesn't
- know how to do this, but that's not the point.
- 4) Close and save your changes.
- 5) Return to Alpha and write a dummy Tcl routine to intercept this event,
- such as
-
- proc snoop {event} {}
-
- It needn't do any more than this. aeparse::event is decidedly not
- your friend for this task (try it if you don't believe me).
- 6) Declare 'snoop' as an AppleEvent handler for the event you wish to
- examine:
-
- eventHandler core getd snoop
-
- 7) Place a trace on 'snoop'.
- 8) Run your modified AppleScript applette from the Finder.
- 9) Return to Alpha and dump traces.
-
- For the case above we obtain
-
- snoop 'core\getd{'----':obj {form:indx, want:type(cdis), seld:abso(«616C6C20»), from:'null'()}, &inte:cans, &timo:3600}'
- snoop OK:
-
- Although this probably seems like a lot of work, I assure you that
- it's far preferable to the hunt-and-peck alternative. There's no
- reason that this methodology won't work with other scriptable
- applications, either, but I take no responsibility for the implosion
- of your monitor.
-
- _________________________________________________________________
-
- Lexicon
- _________________________________________________________________
-
- There are a plethora of routines in these packages, and the
- majority of the parsing routines, in particular, will probably
- never be called directly. The ones most programmers will ever
- have need to call are listed below.
-
- Note: If you do have reason to parse some sub-string of an
- AEDesc, keep in mind that, with the exception of aeparse::event,
- all of the token and grammar parsers operate in place, i.e., they
- are not passed a string, but the name of a string and, when
- finished, the parsed material is removed from the start of the
- string.
- _________________________________________________________________
-
- AEParse
- _________________________________________________________________
-
- NAME
- aeparse::event - Parse a textual event record, as returned by AEPrint.
- SYNOPSIS
- aeparse::event chars ?-all? ?-coerce coercions?
- ?-noCoerce noCoercions?
- _________________________________________________________________
-
- DESCRIPTION
-
- Optional parameters:
-
- -all: Return the entire parsed event structure, not just the AE
- descriptor record. The default behavior omits the class and the
- event. The reply from a call to AEBuild is always(?) an “aevt\ansr”,
- but Alpha event handlers could potentially receive anything and
- may have use for this information.
-
- -coerce: Temporarily override (or supply) the specified type
- coercions. Takes a list of lists, each element of which is
- {'fromType' 'toType' 'coercionProc'}.
-
- -noCoerce: Do not perform the specified type coercions. This is
- useful, for instance, to prevent the automatic coercion of
- 'alis's or 'fss 's to paths; this coercion is time consuming and
- may not always be of interest. Takes a list of lists, each
- element of which is {'fromType' 'toType'}. glob style
- wildcards can be used to block a whole family of coercions,
- e.g., “-noCoerce {{hexd *}}” will prevent all hexd coercions.
-
- _________________________________________________________________
-
- NAME
- aeparse::keywordValue - Return value of keyword from AE record.
- SYNOPSIS
- aeparse::keywordValue keyword record ?typed?
- _________________________________________________________________
-
- DESCRIPTION
-
- Return the value of the specified keyword from a parsed
- AppleEvent record.
-
- Optional parameter:
-
- typed: Boolean flag determines whether the value is returned as a list
- {type value} or simply as value. Default is false.
-
- _________________________________________________________________
-
- AECoerce
- _________________________________________________________________
-
- NAME
- aecoerce::apply - coerce an AE value.
- SYNOPSIS
- aecoerce::apply value type
- _________________________________________________________________
-
- DESCRIPTION
-
- 'value' is coerced to 'type'. 'value' must be a list of
- {'oldType' 'value'}. If a coercion from 'oldType' to 'type' has
- not been declared with aecoerce::register, or with the
- optional -coerce parameter to aeparse::event, then an error is
- thrown.
-
- _________________________________________________________________
-
- NAME
- aecoerce::register - register a coercion procedure.
- SYNOPSIS
- aecoerce::register from to proc
- _________________________________________________________________
-
- DESCRIPTION
-
- Registers 'proc' to handle coercions from AE type 'from' to AE
- type 'to'. Proc must take a single argument of type 'from' and
- return a value of type 'to'. E.g.,
-
- aecoerce::register "hexd" "fss " specToPathName
-
- will handle the coercion of “fss («FFFD000001C…CCCCC»)” to
- {{fss } {documents:Eudora:HIS:In}}.
-
- _________________________________________________________________
-
- AEBuild
- _________________________________________________________________
-
- NAME
- aebuild::result - Build an AppleEvent and return the parsed
- direct object of its result.
- SYNOPSIS
- aebuild::result args
- _________________________________________________________________
-
- DESCRIPTION
-
- The arguments to this command are those of AEBuild.
- AEBuild's ‘-r’ option to is not required, but is not harmful,
- either. The ‘-q’ option is not meaningful in this context,
- and an “Unexpected end of format string” error will be thrown.
-
- There is no mechanism for overriding coercions with this call. If
- you wish to do that, you will need to use the long-hand
-
- aeparse::keywordValue ---- [aeparse::event [AEBuild -r ...] -coerce ...]
-
- _________________________________________________________________
-
- NAME
- aebuild::objectProperty - Return specified property.
- SYNOPSIS
- aebuild::objectProperty process property object
- _________________________________________________________________
-
- DESCRIPTION
-
- Ask 'process' for 'property' of 'object'. There is no mechanism for
- overriding coercions with this call.
-